package com.sweets.look.util;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.sweets.look.constant.Constants;
import com.sweets.look.ui.activity.CrashActivity;
import com.sweets.look.ui.App;

@SuppressLint(value = {"NewApi"})
public final class CrashUtils {
    public static interface EventListener extends Serializable {
        void onCloseAppFromErrorActivity();

        void onLaunchErrorActivity();

        void onRestartAppFromErrorActivity();
    }

    private static final String CAOC_HANDLER_PACKAGE_NAME = "com.sweets.look.util.CrashUtils";
    private static final String DEFAULT_HANDLER_PACKAGE_NAME = "com.android.internal.os";
    private static final String EXTRA_EVENT_LISTENER = "com.sweets.look.util.EXTRA_EVENT_LISTENER";
    private static final String EXTRA_RESTART_ACTIVITY_CLASS = "com.sweets.look.util.EXTRA_RESTART_ACTIVITY_CLASS";
    private static final String EXTRA_STACK_TRACE = "com.sweets.look.util.EXTRA_STACK_TRACE";
    private static final String INTENT_ACTION_ERROR_ACTIVITY = "com.sweets.look.util.ERROR";
    private static final String INTENT_ACTION_RESTART_ACTIVITY = "com.sweets.look.util.RESTART";
    private static final int MAX_STACK_TRACE_SIZE = 131071;
    private static final String SHARED_PREFERENCES_FIELD_TIMESTAMP = "last_crash_timestamp";
    private static final String SHARED_PREFERENCES_FILE = "custom_activity_on_crash";
    private static final String TAG = "CrashUtils";
    private static final int TIMESTAMP_DIFFERENCE_TO_AVOID_RESTART_LOOPS_IN_MILLIS = 2000;
    private static boolean enableAppRestart;
    private static Class errorActivityClass;
    private static EventListener eventListener;
    private static boolean isInBackground;
    private static WeakReference<Activity> lastActivityCreated;
    private static boolean launchErrorActivityWhenInBackground;
    private static Class restartActivityClass;

    static {
        lastActivityCreated = new WeakReference<>(null);
        isInBackground = false;
        launchErrorActivityWhenInBackground = true;
        enableAppRestart = true;
        errorActivityClass = null;
        restartActivityClass = null;
        eventListener = null;
    }


    private static String capitalize(String s) {
        if (s == null) {
            return "";
        }

        if (s.length() != 0) {
            char v0 = s.charAt(0);
            if (Character.isUpperCase(v0)) {
                return s;
            }

            s = Character.toUpperCase(v0) + s.substring(1);
        } else {

            s = "";
        }

        return s;
    }

    public static void closeApplication(Activity activity, EventListener eventListener) {
        if (eventListener != null) {
            eventListener.onCloseAppFromErrorActivity();
        }

        activity.finish();
        killCurrentProcess();
    }

    private static String getDeviceModelName() {
        String v0 = Build.MANUFACTURER;
        String v1 = Build.MODEL;
        String v2 = v1.startsWith(v0) ? capitalize(v1) : capitalize(v0) + " " +
                v1;
        return v2;
    }

    private static Class getErrorActivityClassWithIntentFilter(Context context) {
        List<ResolveInfo> v2 = context.getPackageManager().queryIntentActivities(new Intent().setAction(INTENT_ACTION_ERROR_ACTIVITY)
                .setPackage(context.getPackageName()), PackageManager.GET_RESOLVED_FILTER);//64
        if (v2 != null && v2.size() > 0) {
            ResolveInfo v1 = v2.get(0);
            try {
                Class v4 = Class.forName(((ResolveInfo) v1).activityInfo.name);
                return v4;
            } catch (ClassNotFoundException v0) {
                Log.e(TAG, "Failed when resolving the error activity class intent filter, stack trace follows!", ((
                        Throwable) v0));
            }
        }

        return null;
    }

    public static Map<String, String> getErrorUploadMap(Context context, Intent intent) {
        HashMap<String, String> v0 = new HashMap();
        ((Map) v0).put("versionCode", String.valueOf(Utils.getAppVersionCode(context)));
        ((Map) v0).put("versionName", Utils.getAppVersionName(context));
        ((Map) v0).put("deviceName", getDeviceModelName());
        ((Map) v0).put("androidVersion", String.valueOf(Constants.API));
        ((Map) v0).put("details", InfoUtils.getInfoBase64Code(context, getStackTraceFromIntent(
                intent)));
        return ((Map) v0);
    }

    public static EventListener getEventListenerFromIntent(Intent intent) {
        Serializable v0 = intent.getSerializableExtra(EXTRA_EVENT_LISTENER);
        if (v0 == null || !(v0 instanceof EventListener)) {
            v0 = null;
        }

        return ((EventListener) v0);
    }

    private static long getLastCrashTimestamp(Context context) {
        return context.getSharedPreferences(SHARED_PREFERENCES_FILE, 0).getLong(SHARED_PREFERENCES_FIELD_TIMESTAMP,
                -1);
    }

    private static Class getLauncherActivity(Context context) {
        Intent v1 = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
        if (v1 != null) {
            try {
                Class v2 = Class.forName(v1.getComponent().getClassName());
                return v2;
            } catch (ClassNotFoundException v0) {
                Log.e(TAG, "Failed when resolving the restart activity class  getLaunchIntentForPackage, stack trace follows!", ((
                        Throwable) v0));
            }
        }

        return null;
    }

    public static Class getRestartActivityClassFromIntent(Intent intent) {

        Serializable v0 = intent.getSerializableExtra(EXTRA_RESTART_ACTIVITY_CLASS);
        if (v0 == null || !(v0 instanceof Class)) {
            v0 = null;
        }

        return (Class) v0;
    }

    private static Class getRestartActivityClassWithIntentFilter(Context context) {
        List<ResolveInfo> v2 = context.getPackageManager().queryIntentActivities(new Intent().setAction(INTENT_ACTION_RESTART_ACTIVITY)
                .setPackage(context.getPackageName()), PackageManager.GET_RESOLVED_FILTER);//64
        if (v2 != null && v2.size() > 0) {
            ResolveInfo v1 = v2.get(0);
            try {
                Class v4 = Class.forName(((ResolveInfo) v1).activityInfo.name);
                return v4;
            } catch (ClassNotFoundException v0) {
                Log.e(TAG, "Failed when resolving the restart activity class intent filter, stack trace follows!", ((
                        Throwable) v0));
            }
        }

        return null;
    }

    public static String getStackTraceFromIntent(Intent intent) {
        return intent.getStringExtra(EXTRA_STACK_TRACE);
    }

    private static Class guessErrorActivityClass(Context context) {
        Class v0 = getErrorActivityClassWithIntentFilter(context);
        if (v0 == null) {
            v0 = CrashActivity.class;
        }

        return v0;
    }

    private static Class guessRestartActivityClass(Context context) {
        Class v0 = getRestartActivityClassWithIntentFilter(context);
        if (v0 == null) {
            v0 = getLauncherActivity(context);
        }

        return v0;
    }

    private static boolean hasCrashedInTheLastSeconds(Context context) {
        long lastCrashTimestamp = getLastCrashTimestamp(context);
        long curTime = new Date().getTime();
        return !(lastCrashTimestamp > curTime ||
                curTime - lastCrashTimestamp >= TIMESTAMP_DIFFERENCE_TO_AVOID_RESTART_LOOPS_IN_MILLIS);
    }

    public static void install() {
        try {
            if (Build.VERSION.SDK_INT < 14) {
                Log.w(TAG, "CrashUtils will be installed, but may not be reliable in API lower than 14");
            }

            final Thread.UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();
            if (oldHandler != null && (oldHandler.getClass().getName().startsWith(CAOC_HANDLER_PACKAGE_NAME))) {
                Log.e(TAG, "You have already installed CrashUtils, doing nothing!");
                return;
            }

            if (oldHandler != null && !oldHandler.getClass().getName().startsWith(DEFAULT_HANDLER_PACKAGE_NAME)) {
                Log.e(TAG, "IMPORTANT WARNING! You already have an UncaughtExceptionHandler, are you sure this is correct? If you use ACRA, Crashlytics or similar libraries, you must initialize them AFTER CrashUtils! Installing anyway, but your original handler will not be called.");
            }

            final Context application = App.getAppContext();
            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread thread, Throwable throwable) {
                    Log.e(TAG, "App has crashed, executing CrashUtils\'s UncaughtExceptionHandler", throwable);
                    if (hasCrashedInTheLastSeconds(application)) {
                        Log.e(TAG, "App already crashed in the last 2 seconds, not starting custom error activity because we could enter a restart loop. Are you sure that your app does not crash directly on init?",
                                throwable);
                        if (oldHandler != null) {
                            oldHandler.uncaughtException(thread, throwable);
                        } else {
                            Activity v2 = lastActivityCreated.get();
                            if (v2 != null) {
                                v2.finish();
                                lastActivityCreated.clear();
                            }

                            killCurrentProcess();
                            return;
                        }
                    } else {
                        setLastCrashTimestamp(application, new Date().getTime());
                        if (errorActivityClass == null) {
                            errorActivityClass = guessErrorActivityClass(application);
                        }

                        if (isStackTraceLikelyConflictive(throwable, errorActivityClass)) {
                            Log.e(TAG, "Your application class or your error activity have crashed, the custom activity will not be launched!");
                            if (oldHandler != null) {
                                oldHandler.uncaughtException(thread, throwable);
                                return;
                            }
                        } else if ((launchErrorActivityWhenInBackground) || !isInBackground) {
                            Intent intent = new Intent(application, errorActivityClass);
                            StringWriter writer = new StringWriter();
                            throwable.printStackTrace(new PrintWriter(writer));
                            String v4 = writer.toString();
                            if (v4.length() > MAX_STACK_TRACE_SIZE) {
                                v4 = v4.substring(0, MAX_STACK_TRACE_SIZE - " [stack trace too large]".length()) + " [stack trace too large]";
                            }

                            if (enableAppRestart && restartActivityClass == null) {
                                restartActivityClass = guessRestartActivityClass(application);
                            } else if (!enableAppRestart) {
                                restartActivityClass = null;
                            }

                            intent.putExtra(EXTRA_STACK_TRACE, v4);
                            intent.putExtra(EXTRA_RESTART_ACTIVITY_CLASS, restartActivityClass);
                            intent.putExtra(EXTRA_EVENT_LISTENER, eventListener);
                            intent.setFlags(268468224);
                            if (eventListener != null) {
                                eventListener.onLaunchErrorActivity();
                            }

                            application.startActivity(intent);
                        }

                        Activity v2 = lastActivityCreated.get();
                        if (v2 != null) {
                            v2.finish();
                            lastActivityCreated.clear();
                        }

                        killCurrentProcess();
                        return;
                    }
                }
            });
            if (Build.VERSION.SDK_INT >= 14) {
                ((Application) application).registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks
                        () {
                    int currentlyStartedActivities;

                    @Override
                    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                        if (activity.getClass() != errorActivityClass) {
                            lastActivityCreated = new WeakReference<Activity>(activity);
                        }
                    }

                    @Override
                    public void onActivityDestroyed(Activity activity) {
                    }

                    @Override
                    public void onActivityPaused(Activity activity) {
                    }

                    @Override
                    public void onActivityResumed(Activity activity) {
                    }

                    @Override
                    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
                    }

                    @Override
                    public void onActivityStarted(Activity activity) {
                        ++this.currentlyStartedActivities;
                        isInBackground = this.currentlyStartedActivities == 0;
                    }

                    public void onActivityStopped(Activity activity) {
                        --this.currentlyStartedActivities;
                        isInBackground = this.currentlyStartedActivities == 0;
                    }
                });
            }

            Log.i(TAG, "CrashUtils has been installed.");
        } catch (Throwable v2) {
            Log.e(TAG, "An unknown error occurred while installing CrashUtils, it may not have been properly initialized. Please report this as a bug if needed.",
                    v2);
        }
    }

    private static boolean isStackTraceLikelyConflictive(Throwable throwable, Class arg8) {

        StackTraceElement[] v1 = throwable.getStackTrace();
        int v4 = v1.length;
        int v3 = 0;

        while (v3 < v4) {
            StackTraceElement v0 = v1[v3];
            if ((v0.getClassName().equals("android.app.ActivityThread")) && (v0.getMethodName().equals(
                    "handleBindApplication"))) {
                return true;
            }

            if (v0.getClassName().equals(arg8.getName())) {
                return true;
            }

            ++v3;
        }

        throwable = throwable.getCause();
        if (throwable != null) {
            return isStackTraceLikelyConflictive(throwable, arg8);
        }

        return false;
    }

    private static void killCurrentProcess() {
        Process.killProcess(Process.myPid());
        System.exit(10);
    }

    public static void restartApplicationWithIntent(Activity activity, Intent intent, EventListener
            eventListener) {
        intent.addFlags(268468224);
        if (eventListener != null) {
            eventListener.onRestartAppFromErrorActivity();
        }

        activity.finish();
        activity.startActivity(intent);
        killCurrentProcess();
    }

    private static void setLastCrashTimestamp(Context context, long timestamp) {
        context.getSharedPreferences(SHARED_PREFERENCES_FILE, 0).edit().putLong(SHARED_PREFERENCES_FIELD_TIMESTAMP,
                timestamp).apply();
    }
}

